//
// See the file COPYING for redistribution information.
//

#ifndef __NAIPAX_CONNECTION_HPP__
#define __NAIPAX_CONNECTION_HPP__

#include <string>
#include <set>
#include <stdint.h>
#include <poll.h>
#include <Naipax/Url.hpp>
#include <Naipax/Ares.hpp>

namespace Naipax {
    /// The members of ConnectionBuffer are transparent. Use with caution.
    class ConnectionBuffer {
    public:
        static const uint32_t STATUS_INIT       = 0;
        static const uint32_t STATUS_UNRESOLVED = (1 << 0);
        static const uint32_t STATUS_RESOLVING  = (1 << 1);
        static const uint32_t STATUS_RESOLVED   = (1 << 2);
        static const uint32_t STATUS_CONNECTING = (1 << 3);
        static const uint32_t STATUS_CONNECTED  = (1 << 4);
        static const uint32_t STATUS_WRITTEN    = (1 << 5);
        static const uint32_t STATUS_COMPLETED  = (1 << 6);

        ConnectionBuffer();

        // The following methods are inlined since they don't span
        // across the whole source code. They are used only in
        // Connection::OneNbLoop().
        inline void Reset();
        inline void WriteData();
        inline void ReadData();
        inline void Connect();
        inline void BuildRequest();
        
        uint32_t mStatus;
        int32_t mFd;
        Naipax::Url mUrl;
        Naipax::AresData mAresData;
        std::string mWriteBuf;
        uint32_t mWriteCurPos;
        bool mGotStatusCode;
        std::string mReadBuf;
        time_t mExpireTime;
        std::string *mpUserAgent;
        std::set<uint32_t> *mpAcceptedStatus;
    };

    /** This class makes concurrent connections to remote HTTP servers
     * and retrieve web pages.
     */
    class Connection {
    public:
        /** Constructor
         *
         * @param MaxConnCount The maximum number of concurrent connections.
         * @exception Error::CANNOT_INIT_CONNECTION when error occurs
         */
        Connection(uint32_t MaxConnCount = 256);

        /// Destructor
        ~Connection();

        /** Add a new connection
         *
         * @param rUrl Url to be fetched
         * @param Timeout Default timeout is 60 seconds
         * @exception Error::DATA_NOT_ACCEPTED if there is no element
         *            in _mAcceptedStatus.
         * @exception Error::BUFFER_IS_FULL if there is no space for
         *            new connections.
         */
        void Add(const std::string &rUrl, time_t Timeout = 30);

        /// Scan the connection buffer to see if there is anything
        /// need to be done.
        void OneNbLoop();

        /// The count of current connections.
        uint32_t CurConnCount();

        /// The maximum count of connections.
        inline uint32_t MaxConnCount() { return _mMaxConnCount; }

        /// The count of successfully fetched web pages
        inline uint64_t CompletedCount() { return _mCompletedCount; }

        /// Accessor to user agent string
        std::string& UserAgent() { return _mUserAgent; }

        /** Accessor to accepted status
         *
         * The default set is { 200 }.
         */
        std::set<uint32_t>& AcceptedStatus() { return _mAcceptedStatus; }

        /// Response handler.
        void (*mpResponseHandler)(Connection &rConn,
                                  std::string &rUrl, std::string &rResponse);

        /// The common argument with which user can carry self-defined
        /// data to response handler. It is transparent. Use with
        /// caution.
        void *mpResponseArg;

    private:
        uint64_t _mCompletedCount;
        uint32_t _mMaxConnCount;
        ConnectionBuffer *_mpConnBuf;
        Ares _mAres;
        std::string _mUserAgent;
        struct pollfd *_mpPollFds;
        std::set<uint32_t> _mAcceptedStatus;
    };
};

#endif // __NAIPAX_CONNECTION_HPP__
